#include "filedownload.h"

FileDownload::FileDownload(QWidget *parent)
	: QWidget(parent)
	, ui(new Ui::FileDownloadClass())
{
	ui->setupUi(this);
	connectDialog_ = nullptr;
	downloadFileDialog_ = nullptr;
	recvState_ = RecvState::RECV_HEADER;
	recvHeader_ = std::make_shared<MsgNode>(MSG_HEADER_LEN);
	//ui->reciveEdit->setEnabled(false);
	this->init();
}

void FileDownload::init()
{
	ui->pathLab->setText("下载地址");
	ui->bytesSend->setText("0");
	ui->bytesRecive->setText("0");
	session_ = std::make_shared<Session>(nullptr);
	connect(ui->connectBtn, &QPushButton::clicked, this, &FileDownload::onConnectBtnClick);
	connect(ui->downloadBtn, &QPushButton::clicked, this, &FileDownload::onDownloadBtnClick);
	connect(ui->uploadBtn, &QPushButton::clicked, this, &FileDownload::onUploadBtnClick);
	connect(ui->clearBtn, &QPushButton::clicked, this, &FileDownload::onClearBtnClick);
	connect(ui->sendBtn, &QPushButton::clicked, this,&FileDownload::onSendBtnClick);
	connect(session_.get(), &Session::readyRead, this, &FileDownload::onReadyRead);
	connect(session_.get(), &Session::sigUpdateRecvData, this, &FileDownload::onSigUpdateRecvData);
}

void FileDownload::flushFileTree()
{
	QString filePath = QString(FILE_PATH) + "/fileTree.txt";
	if (QFile::exists(QString(filePath))) {
		QFile::remove(filePath);
	}
	QFile file(filePath);
	if (file.isOpen()) {
		file.close();
	}
	Json::Value requestObj;
	requestObj["type"] = static_cast<int>(FileType::TYPE_GET_FILE_TREE);
	Json::StreamWriterBuilder builder;
	std::string requestBody = Json::writeString(builder, requestObj);
	std::shared_ptr<SendNode> sendNode = std::make_shared<SendNode>(requestBody.data(), static_cast<int>(MsgType::TYPE_FILE), requestBody.size());
	bool res = session_->write(sendNode);
	if (!res) {
		QMessageBox::critical(nullptr, "请求文件树", "请求文件树发送失败");
	}
}


FileDownload::~FileDownload()
{
	delete ui;
}

void FileDownload::updateFileTree(Json::Value& fileTreeObj)
{
	downloadFileDialog_->setFileTree(fileTreeObj);
}

void FileDownload::onConnectBtnClick()
{
	if (connectDialog_ == nullptr) {
		connectDialog_ = new ConnectDialog(this);

	}
	int res = connectDialog_->exec();
	try {
		switch (res)
		{
		case ConnectType::CONNECT:
			session_->connectToHost(connectDialog_->getIp(), connectDialog_->getPort());
			break;
		case ConnectType::DISCONNECT:
			session_->disconnectFromHost();
			break;
		case ConnectType::UNKNOWN_TYPE:
		default:
			break;
		}
	}
	catch (const std::exception& e) {
		qDebug() << e.what();
	}
}

void FileDownload::onSigUpdateRecvData(const QString& data)
{
	std::lock_guard<std::mutex> locker(recvLocker_); // 加锁，防止多个线程同时更新接收数据导致竞争
	QString oldText = ui->reciveEdit->toPlainText();
	QString newText = oldText + data;
	ui->reciveEdit->setPlainText(newText);
}


void FileDownload::onDownloadBtnClick()
{
	ui->pathLab->setText("下载路径:");
	if (downloadFileDialog_ == nullptr) {
		downloadFileDialog_ = new DownloadFileDialog(this);
		connect(downloadFileDialog_, &DownloadFileDialog::sigSetDownloadPath, [&](const QString& downloadPath) {
			downloadPath_ = downloadPath;
			ui->pathEdit->setText(downloadPath_);
			});
		connect(downloadFileDialog_, &DownloadFileDialog::sigFlushFileTree, this, &FileDownload::flushFileTree);

	}
	int res = downloadFileDialog_->exec();
	if (res == static_cast<int>(DownLoadType::CANCEL)) {
		return;
	}
	// 这里假设要获取文件是 知识星球：开发一个WebServer.pdf
	std::string file = "知识星球：开发一个WebServer.pdf";
	Json::Value jsonObj;
	jsonObj["seq"] = 0;
	jsonObj["filename"] = file;
	jsonObj["type"] = static_cast<int>(FileType::TYPE_FILE_DOWNLOAD);
	jsonObj["hasRecv"] = 0;
	short msgId = static_cast<int>(MsgType::TYPE_FILE);
	Json::StreamWriterBuilder builder;
	std::string jsonStr = Json::writeString(builder, jsonObj);
	std::shared_ptr<SendNode> sendNode = std::make_shared<SendNode>(jsonStr.data(), msgId, jsonStr.size());
	session_->write(sendNode);
}

void FileDownload::onUploadBtnClick()
{
	ui->pathLab->setText("上传文件:");
	QString fileName = QFileDialog::getOpenFileName(nullptr, "文件路径", "C:/");
	ui->pathEdit->setText(fileName);
}

void FileDownload::onClearBtnClick()
{
	ui->sendEdit->clear();
	ui->reciveEdit->clear();
	ui->bytesSend->setText("0");
	ui->bytesRecive->setText("0");
}

void FileDownload::onSendBtnClick()
{
	if (session_->state() != QTcpSocket::ConnectedState) {
		QMessageBox::critical(nullptr, "send", "未连接到服务器");
		return;
	}
	QString textToSend = ui->sendEdit->toPlainText();
	QByteArray bytesToSend = textToSend.toUtf8();
	short msgId = 1;
	unsigned long long msgDataLen = bytesToSend.size();
	std::shared_ptr<SendNode> sendNode = std::make_shared<SendNode>(bytesToSend.data(), msgId, msgDataLen);
	QString bytesSend = QString::asprintf("%1").arg(ui->bytesSend->text().toInt() + bytesToSend.size());
	ui->bytesSend->setText(bytesSend);
	bool res = session_->write(sendNode);
	if (res < 0) {
		QMessageBox::critical(nullptr, "发送数据", "向服务器发送数据失败！");
	}
}

// 先读取header，在根据header中len值创建缓冲区读取数据包的body
void FileDownload::onReadyRead()
{
	bool readable = true;
	while (readable) {
		// 读入包头
		if (recvState_ == RecvState::RECV_HEADER) {
			size_t bytes = session_->read(recvHeader_->data_ + recvHeader_->curLen_, MSG_HEADER_LEN - recvHeader_->curLen_);
			if (bytes <= 0) { // 读取出问题
				if (bytes < 0) {
					QMessageBox::critical(nullptr, "read", "read error");
				}
				readable = false;
				break;
			}
			recvHeader_->curLen_ += bytes;
			if (recvHeader_->curLen_ == MSG_HEADER_LEN) {
				//memcpy(recvHeader_->data_ + recvHeader_->curLen_, buffer_, bytes);
				// 读取完毕，处理包头，初始化recvBody_用于读取包体
				short msgId = 0;
				int64_t dataLen = 0;
				memcpy(&msgId, recvHeader_->data_, sizeof(short));
				memcpy(&dataLen, recvHeader_->data_ + MSG_ID_LEN, sizeof(int64_t));
				//memcpy(&fileTotalLen_, recvHeader_->data_ + MSG_ID_LEN, sizeof(int64_t)); // note: 析构时报错 Run-Time Check Failure #2 - Stack around the variable 'w' was corrupted，因为 fileTotalLen_（size_t）只占4B
				msgId = ntohs(msgId);
				fileTotalLen_ = ntohl(dataLen);
				fileRecvLen_ = 0;
				qDebug() << "recv data msgId: " << msgId << " dataLen: " << fileTotalLen_;
				// 数据包体最大长度为 MSG_MAX_DATA_LEN，对于大文件传输时，需要拆分成多个 RecvNode
				recvBody_ = std::make_shared<RecvNode>(msgId, fileTotalLen_ < MSG_MAX_DATA_LEN ? fileTotalLen_ : MSG_MAX_DATA_LEN);
				recvState_ = RecvState::RECV_BODY;
				// 重置包头部接收节点
				recvHeader_->clear();
				//recvHeader_ = std::make_shared<MsgNode>(MSG_HEADER_LEN);
			}
			else if(recvHeader_->curLen_ < MSG_HEADER_LEN) { // 读取的字节小于头部，继续读
				continue;
			}
			else { // 读取的长度大于 MSG_HEADER_LEN ,报错
				QMessageBox::critical(nullptr, "read", "read error");
				return;
			}
		}
		// 读取数据包body部分		
		if (recvState_ == RecvState::RECV_BODY) {
			size_t bytes = session_->read(recvBody_->data_ + recvBody_->curLen_, recvBody_->totalLen_ - recvBody_->curLen_);
			if (bytes <= 0) { // 读取出问题
				if (bytes < 0) {
					QMessageBox::critical(nullptr, "read", "read error");
				}
				readable = false;
				break;
			}
			recvBody_->curLen_ += bytes;
			if (recvBody_->curLen_ == recvBody_->totalLen_) { // 读取结束
				// TYPE_TEXT类型、TYPE_FILE类型的包处理暂时都放在同一个线程池中
				fileRecvLen_ += recvBody_->totalLen_;
				size_t byteNeedRead = fileTotalLen_ - fileRecvLen_;
				short msgId = recvBody_->msgId_;
				LogicSystem::getInstance().postTask(0, std::make_shared<LogicNode>(session_, recvBody_));
				recvBody_.reset();
				if (byteNeedRead == 0) { // 文件读取结束
					recvState_ = RecvState::RECV_HEADER;
				}
				else if (byteNeedRead > 0) { // 文件未读取结束，需要继续读取下一个recvNode
					recvBody_ = std::make_shared<RecvNode>(msgId, byteNeedRead < MSG_MAX_DATA_LEN ? byteNeedRead : MSG_MAX_DATA_LEN);
				}
				else { // 异常
					QMessageBox::critical(nullptr, "read", "read error");
					break;
				}
			}
			else if (recvBody_->curLen_ > recvBody_->totalLen_) { // 读取异常
				QMessageBox::critical(nullptr, "read", "read error");
				break;
			}
			else { // 继续读取
				continue;
			}
		}
	}
}
